package com.denlaku.longan.service.impl;

import com.denlaku.longan.anno.RequiredTx;
import com.denlaku.longan.qo.Query;
import com.denlaku.longan.repository.ComponentRepository;
import com.denlaku.longan.service.ComponentService;
import com.denlaku.longan.service.ConditionService;
import com.denlaku.longan.service.DimensionService;
import com.denlaku.longan.service.GoingInService;
import com.denlaku.longan.service.LinkageService;
import com.denlaku.longan.service.MeasureService;
import com.denlaku.longan.vo.Component;
import com.denlaku.longan.vo.Condition;
import com.denlaku.longan.vo.Dimension;
import com.denlaku.longan.vo.GoingIn;
import com.denlaku.longan.vo.Linkage;
import com.denlaku.longan.vo.Measure;
import org.apache.commons.collections4.ListUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author tianx
 */
@Service
public class ComponentServiceImpl implements ComponentService {
    @Autowired
    private ComponentRepository componentRepository;

    @Autowired
    private DimensionService dimensionService;

    @Autowired
    private MeasureService measureService;

    @Autowired
    private LinkageService linkageService;

    @Autowired
    private GoingInService goingInService;

    @Autowired
    private ConditionService conditionService;

    @Override
    @RequiredTx
    public void batchAdd(List<Component> componentList) {
        componentRepository.batchAdd(componentList);
        addDimensions(componentList);
        addMeasures(componentList);
        addLinkages(componentList);
        addGoingIns(componentList);
        addFilters(componentList);
    }

    private void addDimensions(List<Component> componentList) {
        List<Dimension> dimensionList = new ArrayList<>();
        for (Component component : ListUtils.emptyIfNull(componentList)) {
            for (Dimension dimension : ListUtils.emptyIfNull(component.getDimensions())) {
                dimension.setComponentId(component.getId());
                dimensionList.add(dimension);
            }
        }
        dimensionService.batchAdd(dimensionList);
    }

    private void addMeasures(List<Component> componentList) {
        List<Measure> measureList = new ArrayList<>();
        for (Component component : ListUtils.emptyIfNull(componentList)) {
            for (Measure measure : ListUtils.emptyIfNull(component.getMeasures())) {
                measure.setComponentId(component.getId());
                measureList.add(measure);
            }
        }
        measureService.batchAdd(measureList);
    }

    private void addLinkages(List<Component> componentList) {
        List<Linkage> linkageList = new ArrayList<>();
        for (Component component : ListUtils.emptyIfNull(componentList)) {
            for (Linkage linkage : ListUtils.emptyIfNull(component.getLinkages())) {
                linkage.setComponentId(component.getId());
                linkageList.add(linkage);
            }
        }
        linkageService.batchAdd(linkageList);
    }

    private void addGoingIns(List<Component> componentList) {
        List<GoingIn> goingInList = new ArrayList<>();
        for (Component component : ListUtils.emptyIfNull(componentList)) {
            for (GoingIn goingIn : ListUtils.emptyIfNull(component.getGoingIns())) {
                goingIn.setComponentId(goingIn.getId());
                goingInList.add(goingIn);
            }
        }
        goingInService.batchAdd(goingInList);
    }

    private void addFilters(List<Component> componentList) {
        List<Condition> conditionList = new ArrayList<>();
        for (Component component : ListUtils.emptyIfNull(componentList)) {
            for (Condition filter : ListUtils.emptyIfNull(component.getFilters())) {
                filter.setComponentId(component.getId());
                conditionList.add(filter);
            }
        }
        conditionService.batchAdd(conditionList);
    }

    @Override
    @RequiredTx
    public void batchDelete(Long tenantId, Long dashboardId) {
        List<Long> componentIds = componentRepository.listComponentIds(tenantId, dashboardId);
        componentRepository.delete(tenantId, dashboardId);
        dimensionService.delete(tenantId, componentIds);
        measureService.delete(tenantId, componentIds);
        linkageService.delete(tenantId, componentIds);
        goingInService.delete(tenantId, componentIds);
        conditionService.delete(tenantId, componentIds);
    }

    @Override
    public Component get(Query query) {
        Component component = componentRepository.get(query);
        if (component != null) {
            Long tenantId = query.getTenantId();
            fillComponents(tenantId, Collections.singletonList(component));
        }
        return component;
    }

    private Map<Long, List<Dimension>> getDimensionMap(Long tenantId, List<Long> componentIds) {
        List<Dimension> dimensions = dimensionService.list(tenantId, componentIds);
        Map<Long, List<Dimension>> dimensionMap = new HashMap<>(dimensions.size());
        for (Dimension dimension : dimensions) {
            dimensionMap.computeIfAbsent(dimension.getComponentId(), k -> new ArrayList<>()).add(dimension);
        }
        return dimensionMap;
    }

    private Map<Long, List<Measure>> getMeasureMap(Long tenantId, List<Long> componentIds) {
        List<Measure> measures = measureService.list(tenantId, componentIds);
        Map<Long, List<Measure>> measureMap = new HashMap<>(measures.size());
        for (Measure measure : measures) {
            measureMap.computeIfAbsent(measure.getComponentId(), k -> new ArrayList<>()).add(measure);
        }
        return measureMap;
    }

    private Map<Long, List<Linkage>> getLinkageMap(Long tenantId, List<Long> componentIds) {
        List<Linkage> linkages = linkageService.list(tenantId, componentIds);
        Map<Long, List<Linkage>> linkageMap = new HashMap<>(linkages.size());
        for (Linkage linkage : linkages) {
            linkageMap.computeIfAbsent(linkage.getComponentId(), k -> new ArrayList<>()).add(linkage);
        }
        return linkageMap;
    }

    private Map<Long, List<GoingIn>> getGoingInMap(Long tenantId, List<Long> componentIds) {
        List<GoingIn> goingIns = goingInService.list(tenantId, componentIds);
        Map<Long, List<GoingIn>> goingInMap = new HashMap<>(goingIns.size());
        for (GoingIn goingIn : goingIns) {
            goingInMap.computeIfAbsent(goingIn.getComponentId(), k -> new ArrayList<>()).add(goingIn);
        }
        return goingInMap;
    }

    private void fillComponents(Long tenantId, List<Component> components) {
        List<Long> componentIds = ListUtils.emptyIfNull(components).stream().map(Component::getId)
            .collect(Collectors.toList());
        Map<Long, List<Dimension>> dimensionMap = getDimensionMap(tenantId, componentIds);
        Map<Long, List<Measure>> measureMap = getMeasureMap(tenantId, componentIds);
        Map<Long, List<Linkage>> linkageMap = getLinkageMap(tenantId, componentIds);
        Map<Long, List<GoingIn>> goingInMap = getGoingInMap(tenantId, componentIds);
        for (Component component : components) {
            Long id = component.getId();
            component.setDimensions(ListUtils.emptyIfNull(dimensionMap.get(id)));
            component.setMeasures(ListUtils.emptyIfNull(measureMap.get(id)));
            component.setLinkages(ListUtils.emptyIfNull(linkageMap.get(id)));
            component.setGoingIns(ListUtils.emptyIfNull(goingInMap.get(id)));
        }
    }

    @Override
    public List<Component> list(Long tenantId, Long dashboardId) {
        List<Component> components = componentRepository.list(tenantId, dashboardId);
        fillComponents(tenantId, components);
        return components;
    }

}
